#region License

// Copyright (c) 2008, BXO Solutions AB
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions are met:
// 
//     * Redistributions of source code must retain the above copyright notice, 
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright 
//       notice, this list of conditions and the following disclaimer in the 
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the BXO Solutions AB nor the names of its 
//       contributors may be used to endorse or promote products derived from 
//       this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#endregion

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Castle.ActiveRecord;

namespace Bxo.TestingPortal.Entities {
    /// <summary>
    /// Represents the project.
    /// </summary>
    [ActiveRecord]
    public class Project {
        private IList<Category> categories;

        private DateTime creationDateTime;

        private string name;

        private IList<Scenario> scenarios;

        /// <summary>
        /// This member is provided only to satisfy NHibernate requirements and should not be used
        /// by user code.
        /// </summary>
        protected Project() {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Project"/> class 
        /// with the specified name.
        /// </summary>
        /// <param name="name">Name for the project.</param>
        /// <exception cref="ArgumentException"><paramref name="name"/> is <see langword="null"/> or <see cref="string.Empty"/>.</exception>
        public Project(string name) {
            if (string.IsNullOrEmpty(name)) {
                throw new ArgumentException("Name of the project cannot be null or empty.", "name");
            }

            this.name = name;
            this.categories = new List<Category>();
            this.scenarios = new List<Scenario>();
            this.creationDateTime = DateTime.Now;
        }

        /// <summary>
        /// Gets or sets the unique identifier for the project.
        /// </summary>
        /// <value>
        /// Identifier for the project.
        /// </value>
        /// <remarks>
        /// <para>Do not change the value of this property unless you know what you are doing.</para>
        /// </remarks>
        [PrimaryKey]
        public virtual int Id { get; set; }

        /// <summary>
        /// Gets or sets name for this project.
        /// </summary>
        /// <value>
        /// Name for the project.
        /// </value>
        /// <exception cref="ArgumentException"><paramref name="value"/> is <see langword="null"/> or <see cref="string.Empty"/>.</exception>
        [Property]
        public virtual string Name {
            get { return this.name; }
            set {
                if (string.IsNullOrEmpty(value)) {
                    throw new ArgumentException("Name of the project cannot be null or empty.", "value");
                }

                this.name = value;
            }
        }

        /// <summary>
        /// Gets the list of categories defined for the project.
        /// </summary>
        /// <value>
        /// List of categories in the project.
        /// </value>
        [HasMany(Access = PropertyAccess.FieldCamelcase, Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Inverse = true)]
        public virtual IList<Category> Categories {
            get { return new CategoryList(this.categories, this); }
        }

        /// <summary>
        /// Gets the list of scenarios that belong to this project.
        /// </summary>
        /// <value>
        /// List of scenarios defined for the project.
        /// </value>
        [HasMany(Access = PropertyAccess.FieldCamelcase, Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Inverse = true)]
        public virtual IList<Scenario> Scenarios {
            get { return new ScenarioList(this.scenarios, this); }
        }

        /// <summary>
        /// Gets the date and time of the creation of this version.
        /// </summary>
        /// <value>
        /// Date and time of the moment when this version was created.
        /// </value>
        [Property(Access = PropertyAccess.FieldCamelcase)]
        public virtual DateTime CreationDateTime {
            get { return this.creationDateTime; }
        }

        public override string ToString() {
            return this.name;
        }

        #region Nested type: CategoryList

        private class CategoryList : Collection<Category> {
            private readonly Project owner;

            public CategoryList(IList<Category> innerList, Project owner) : base(innerList) {
                this.owner = owner;
            }

            protected override void InsertItem(int index, Category item) {
                if (item == null) {
                    throw new ArgumentNullException("item");
                }
                if (item.Project != null) {
                    throw new InvalidOperationException("Cannot add category because it already belongs to a project.");
                }

                base.InsertItem(index, item);

                item.Project = this.owner;
            }

            protected override void RemoveItem(int index) {
                Category item = this[index];
                if (item.Scenarios.Count > 0) {
                    throw new InvalidOperationException("Category cannot be removed from the project because it contains some scenarios.");
                }

                base.RemoveItem(index);

                item.Project = null;
            }

            protected override void SetItem(int index, Category item) {
                throw new NotSupportedException();
            }

            protected override void ClearItems() {
                Category[] copyOfCategories = this.ToArray();

                base.ClearItems();

                foreach (var category in copyOfCategories) {
                    category.Project = null;
                }
            }
        }

        #endregion

        #region Nested type: ScenarioList

        private class ScenarioList : Collection<Scenario> {
            private readonly Project owner;

            public ScenarioList(IList<Scenario> innerList, Project owner) : base(innerList) {
                this.owner = owner;
            }

            protected override void ClearItems() {
                throw new NotSupportedException("Scenarios cannot be deleted.");
            }

            protected override void InsertItem(int index, Scenario item) {
                base.InsertItem(index, item);

                item.Project = this.owner;
            }

            protected override void RemoveItem(int index) {
                throw new NotSupportedException("Scenarios cannot be deleted.");
            }

            protected override void SetItem(int index, Scenario item) {
                throw new NotSupportedException("Scenarios cannot be deleted.");
            }
        }

        #endregion
    }
}